/*
 * Address.java
 *
 * Created on 02 July 2007, 08:18
 *
 * Copyright (C)
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */

package com.grubby.databean.common;

import com.grubby.databean.AbstractDataBean;
import com.grubby.databean.annotation.SqlTableName;
import com.grubby.utils.StringUtils;
import java.io.Serializable;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author A. HARMEL-LAW. E: andrew.harmel.law@gmail.com
 * @version 0.1
 */
@SqlTableName("ADDRESS")
public class Address extends AbstractDataBean implements Serializable  {
    
    private static final int IN_GROUP_INDEX = 1;
    private static final int OUT_GROUP_INDEX = 3;
    
    // TODO: Serialization Version ID for this clas
    
    /**
     * Postcode validation pattern
     */
    private static final String VALID_POSTCODE_REGEX 
            = "(([A-Z]{1,2})[0-9R][0-9A-Z]?) ([0-9][ABD-HJLNP-UW-Z]{2})";
    
    /** Compiled version of the regular expression for addresses. */
    private static final Pattern VALID_ADDRESS_PATTERN 
            = Pattern.compile(VALID_POSTCODE_REGEX);
    
    /**
     * Address line 1.
     * <p>
     * Mandatory; max length 35 Define max length.
     */
    private String addressLine1 = "";
    
    /**
     * Address line 2.
     * <p>
     * Optional; max length 35. NULL where not held.
     */
    private String addressLine2 = "";
    
    /**
     * Address line 3.
     * <p>
     * Optional; max length 35. NULL where not held.
     */
    private String addressLine3 = "";
    
    /**
     * Address line 4.
     * <p>
     * Optional; max length 35. NULL where not held.
     */
    private String addressLine4 = "";
    
    /**
     * Address line 5.
     * <p>
     * Optional; max length 35. NULL where not held.
     */
    private String addressLine5 = "";
    
    /**
     * Postcode.
     * <p>
     * Optional; max length 8.
     */
    private String postCode = "";
    
    /**
     * Country code. Max Length 3.
     * <p>
     * The country code of the address
     */
    private String country;
    
    /** Defualt country code. */
    public static final String DEFAULT_COUNTRY_CODE = "GBR";
    
    /**
     * Constructor.
     * @param A <HashMap> which maps the address attributes to corresponding SQL 
     * column names.
     * @param A <HashMap> which maps the address attributes to corresponding CSV 
     * column names
     */
    public Address(HashMap sqlColumnNames, HashMap csvColumnNames) {

        sqlColumnNames.put("addressLine1", "ADDRESS_LINE_1");
        sqlColumnNames.put("addressLine2", "ADDRESS_LINE_2");
        sqlColumnNames.put("addressLine3", "ADDRESS_LINE_3");
        sqlColumnNames.put("addressLine4", "ADDRESS_LINE_4");
        sqlColumnNames.put("addressLine5", "ADDRESS_LINE_5");
        sqlColumnNames.put("postCode", "POSTCODE");
        sqlColumnNames.put("country", "COUNTRY_CODE");

    }
    
    /**
     * Constructor
     * @param addressLine1
     * @param addressLine2
     * @param addressLine3
     * @param addressLine4
     * @param addressLine5
     * @param postCode
     * @param country
     * @param A <HashMap> which maps the address attributes to corresponding SQL 
     * column names.
     * @param A <HashMap> which maps the address attributes to corresponding CSV 
     * column names
     */
    public Address(String addressLine1, String addressLine2, String addressLine3, 
            String addressLine4, String addressLine5, String postCode, 
            String country, HashMap sqlColumnNames, HashMap csvColumnNames) {
        
        super();
        this.addressLine1 = addressLine1;
        this.addressLine2 = addressLine2;
        this.addressLine3 = addressLine3;
        this.addressLine4 = addressLine4;
        this.addressLine5 = addressLine5;
        this.postCode = postCode;
        this.country = country;
        
    }
    
    /**
     * @return Returns the addressLine1.
     */
    public String getAddressLine1() {
        return addressLine1;
    }
    
    /**
     * @param addressLine1
     * The addressLine1 to set.
     */
    public void setAddressLine1(String addressLine1) {
        this.addressLine1 = addressLine1;
    }
    
    /**
     * @return Returns the addressLine2.
     */
    public String getAddressLine2() {
        return addressLine2;
    }
    
    /**
     * @param addressLine2
     * The addressLine2 to set.
     */
    public void setAddressLine2(String addressLine2) {
        this.addressLine2 = addressLine2;
    }
    
    /**
     * @return Returns the addressLine3.
     */
    public String getAddressLine3() {
        return addressLine3;
    }
    
    /**
     * @param addressLine3
     * The addressLine3 to set.
     */
    public void setAddressLine3(String addressLine3) {
        this.addressLine3 = addressLine3;
    }
    
    /**
     * @return Returns the addressLine4.
     */
    public String getAddressLine4() {
        return addressLine4;
    }
    
    /**
     * @param addressLine4
     * The addressLine4 to set.
     */
    public void setAddressLine4(String addressLine4) {
        this.addressLine4 = addressLine4;
    }
    
    /**
     * @return Returns the addressLine5.
     */
    public String getAddressLine5() {
        return addressLine5;
    }
    
    /**
     * @param addressLine5
     * The addressLine5 to set.
     */
    public void setAddressLine5(String addressLine5) {
        this.addressLine5 = addressLine5;
    }
    
    /**
     * @return Returns the postCode.
     */
    public String getPostCode() {
        return postCode;
    }
    
    /**
     * @param postCode
     * The postCode to set.
     */
    public void setPostCode(String postCode) {
        this.postCode = postCode;
    }
    
    /**
     * @return Returns the initial characters in the postcode eg on
     * postcode 'AA1 1BB' would return 'AA1' and on postcode 'B1 1CC'
     * would return 'B1'
     */
    public String getOutboundPostCode() {
        return getGroupFromPostcode(IN_GROUP_INDEX);
    }
    
    /**
     * @return Returns the initial alpha characters in the postcode eg on
     * postcode 'AA1 1BB' would return '1BB' and on postcode 'B1 1CC'
     * would return '1CC'
     */
    public String getInboundPostCode() {
        return getGroupFromPostcode(OUT_GROUP_INDEX);
    }
    
    /** 
     * Helper method to retrieve regular expression groups from the postcode. 
     */
    private String getGroupFromPostcode(int group) {
        if (this.postCode != null) {
            Matcher m = VALID_ADDRESS_PATTERN.matcher(this.postCode);
            if (m.find()) {
                return m.group(group);
            } else {
                return null;
            }
        } else {
            return null;
        }
    }
    
    /**
     * Is the value passed to this method a valid postcode with regard to the
     * regular expression contained within this class?
     * <P>
     * @param postcode The postcode to validate
     * @return True if the postcode matches the regular expression
     */
    public static boolean isValidPostCode(String postcode) {
        return Pattern.matches(VALID_POSTCODE_REGEX, postcode);
    }
    
    /**
     * 
     */
    String setOutboundAndInboundPostcodeParts(String outbound, String inbound) {
        setPostCode(outbound + " " + inbound);
        return getPostCode();
    }
    
    public String getOutboundAlphaPostCode() {
        if (this.postCode != null) {
            String alphaOutboundPostcodeRegex = "([a-zA-Z]*)(.*)";
            Pattern pattern = Pattern.compile(alphaOutboundPostcodeRegex);
            Matcher m = pattern.matcher(this.postCode);
            String matchedString = "";
            if (m.matches()) {
                matchedString = m.group(1);
            }
            return matchedString;
        } else {
            return null;
        }
    }
    
    /**
     * @return Returns the country code.
     */
    public String getCountryCode() {
        if (StringUtils.isBlank(country)) {
            return "GBR";
        } else {
            return country;
        }
    }
    
    /**
     * @param country The country to set.
     */
    public void setCountry(String country) {
        this.country = country;
    }
    
    public String getCountry() {
        return country;
    }
    
    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    public boolean equals(Object obj) {
        
        // TODO: Change this to not throw a Runtime exception
        
        if (!(obj instanceof Address)) {
            throw new RuntimeException("Object being compared is not an instance of address");
        } else {
            Address add = (Address) obj;
            return  (addressLine1.equals(add.getAddressLine1()))
            && (addressLine2.equals(add.getAddressLine2()))
            && (addressLine3.equals(add.getAddressLine3()))
            && (addressLine4.equals(add.getAddressLine4()))
            && (addressLine5.equals(add.getAddressLine5()))
            && (postCode.equals(add.getPostCode()))
            && (country.equals(add.getCountry()));
        }
    }
    
    /**
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {
        
        // TODO: Implement this properly
        
        return super.hashCode();
        
    }
}
